package com.kehutong.admin.util.service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.coraframework.cache.Cache;
import org.coraframework.cache.support.NOCache;
import org.coraframework.inject.Inject;
import org.coraframework.inject.Singleton;
import org.coraframework.orm.Entity;
import org.coraframework.orm.EntityCache;
import org.coraframework.orm.Pool;
import org.coraframework.orm.exception.ORMException;
import org.coraframework.orm.jdbc.JdbcSession;
import org.coraframework.orm.jdbc.execute.DataFilter;
import org.coraframework.util.CallBack;
import org.coraframework.util.Objects;

import com.kehutong.admin.entity.Area;
import com.kehutong.admin.entity.Corp;
import com.kehutong.admin.entity.Dict;
import com.kehutong.admin.entity.Menu;
import com.kehutong.admin.entity.Role;
import com.kehutong.admin.entity.TreeResource;
import com.kehutong.admin.entity.User;
import com.kehutong.admin.util.SysDict;
import com.kehutong.admin.util.UserUtils;
import com.kehutong.common.entity.Root;
import com.kehutong.common.filter.FilterFactory;

@Singleton
public class UserService {

	@Inject
	private JdbcSession jdbcSession;

	public List<Role> loadRoles() {
		return loadingAll(Role.class);
	}

	public List<Role> getRoleList(User user) {
		List<Role> roleList = getRoleList();
		if (user.isAdmin()) {
			return roleList;
		}
		
		Set<String> roleIds = user.getRoles();
    	
		roleList = new ArrayList<>(roleIds.size());
    	for (String roleId : roleIds) {
    		roleList.add(Pool.get(Role.class, roleId));
    	}
    	
    	return roleList;
	}
	
	public List<Role> loadRoles(User user) throws Exception {
		if (Objects.isEmpty(user.getRoles())) {
			return Collections.emptyList();
		}
		
		this.loadingFilter(Role.class, null);
		List<Role> roles = new ArrayList<>();
		user.getRoles().forEach(roleId->{
			Role role = Pool.get(Role.class, roleId);
			if (role != null) {
				roles.add(role);
			}
		});
		
		return roles;
	}

	public Map<String, Menu> loadMenus(boolean showAll) throws Exception {
		final Map<String, Menu> map = new HashMap<>();

		this.loadingFilter(Menu.class, (menu)->{
			if (menu.showAll(showAll)) {
				map.put(menu.getId(), menu);
			}
		});

		return map;
	}
	
	public List<Menu> loadMenuList(boolean showAll) throws Exception {
		final List<Menu> menuList = new ArrayList<>(512);

		this.loadingFilter(Menu.class, (menu)->{
			if (menu.showAll(showAll)) {
				menuList.add(menu);
			}
		});

		return menuList;
	}

	public Map<String, Menu> loadMenus(User user,  boolean showAll) throws Exception {
		List<Role> roles = this.loadRoles(user);
		if (Objects.isEmpty(roles)) {
			return Collections.emptyMap();
		}
		this.loadingFilter(Menu.class, null);
		
		if (isAdminRole(roles)) {
			return loadMenus(showAll); // TODO 移除系统修改菜单
		}

		final Map<String, Menu> map = new HashMap<>();
		roles.forEach(role->{
			role.getMenus().forEach(menuId->{
				final Menu menu = Pool.get(Menu.class, menuId);
				if (menu != null && !menu.isDeleted() && menu.showAll(showAll)) {
					map.put(menu.getId(), menu);
				}
			});
		});

		return map;
	}

	public Map<String, Corp> loadOffices() throws Exception {
		final Map<String, Corp> map = new HashMap<>();

		this.loadingFilter(Corp.class, (office)->map.put(office.getId(), office));
		
		return map;
	}

	public Map<String, Area> loadAreas() throws Exception {
		final Map<String, Area> map = new HashMap<>();

		this.loadingFilter(Area.class, (area)->map.put(area.getId(), area));

		return map;
	}
	
	public void flushIP(User user, String loginIp) {
		user.setLogin_ip(loginIp);
		user.setLogin_date(LocalDateTime.now());
		
		jdbcSession.updateById(user);
	}
	
	/**
     * 根据字典值获取该值的描述
     *
     * @param value 字典值
     * @param type  字典类型
     * @return
	 * @throws Exception 
     */
    public String getDictLabel(Object value, String type) throws Exception {
        return getDictLabel(value, type, "");
    }

    /**
     * 根据字典值获取该值的描述
     *
     * @param value        字典值
     * @param type         字典类型
     * @param defaultValue 默认值
     * @return
     * @throws Exception 
     */
    public String getDictLabel(Object value, String type, String defaultValue) throws Exception {
        if (Objects.nonEmpty(type) && Objects.nonEmpty(String.valueOf(value))) {
            for (SysDict dict : getDictList(type)) {
                if (String.valueOf(value).equals(dict.getValue())) {
                    return dict.getLabel();
                }
            }
        }
        return defaultValue;
    }

    /**
     * 获取该类型对应的字典列表
     *
     * @param type 字典类型
     * @return
     * @throws Exception 
     */
    public List<SysDict> getDictList(String type) throws Exception {
        List<SysDict> dictList = getDictMap().get(type);
        if (dictList == null) {
            dictList = new ArrayList<>();
        }
        return dictList;
    }

    public Map<String, List<SysDict>> getDictMap() throws Exception {
        Map<String, List<SysDict>> dictMap =  new HashMap<>();
        
        loadingFilter(Dict.class, (dict)->{
        	 List<SysDict> dictList = dictMap.get(dict.getType());
             if (dictList == null) {
             	dictList = new ArrayList<>();
             	dictMap.put(dict.getType(), dictList);
             }
             
             dictList.add(new SysDict(dict.getLabel(), dict.getValue()));
        });
        
        return dictMap;
    }
    
    public <T extends Entity> void loadingFilter(Class<T> clazz, CallBack<T> call) throws Exception {
    	EntityCache<T> entityCache = jdbcSession.getTemplate(clazz);
    	//没有缓存直接返回
    	if (entityCache == null) {
    		throw new ORMException("no entity cache found for class:" + clazz);
    	}
    	
    	Cache<String, T> dataCache = entityCache.getCache();
    	if (Objects.isEmpty(dataCache)) {
    		entityCache.setCache(dataCache = new NOCache<>());
    		jdbcSession.findArray(clazz)
				.eq("deleted", false)
				.exe();
    	}
    	
    	if (call != null) {
    		for (T item :dataCache.values()) {
    			if (item instanceof TreeResource) {
    				if (!((TreeResource<?>)item).isDeleted()) {
        				call.call(Pool.clone(item));
        			}
    			} else {
    				call.call(Pool.clone(item));
    			}
    		}
    	}
    }
    
    public List<Role> loadingAll(Class<Role> clazz) {
    	EntityCache<Role> entityCache = jdbcSession.getTemplate(clazz);
    	//没有缓存直接返回
    	if (entityCache == null) {
    		throw new ORMException("no entity cache found for class:" + clazz);
    	}
    	
    	List<Role> itemList;
    	Cache<String, Role> dataCache = entityCache.getCache();
    	if (Objects.nonEmpty(dataCache)) {
    		itemList = new ArrayList<>(64);
    		dataCache.values().forEach(item->{
    			if (!item.isDeleted()) {
    				itemList.add(Pool.clone(item));
    			}
    		});
    	} else {
    		entityCache.setCache(new NOCache<>());
    		itemList = jdbcSession.findArray(clazz)
				.eq("deleted", false)
				.exe();
    	}
    	
    	return itemList;
    }
    
	public Map<String, DataFilter> loadPermission(boolean isAdmin, List<String> roleIds) throws Exception {
		if (isAdmin) {
			return Collections.emptyMap();
		}
		
		List<Role> roles = new ArrayList<>(roleIds.size());
		for (String roleId : roleIds) {
			Role role = Pool.get(Role.class, roleId);
			if (role != null && !role.isDeleted()) {
				roles.add(role);
			}
		}
		if (hasRoot(roles)) {
			List<Menu> menuList = getMenuList();
			return toRootPermissions(menuList);
		}
		
		return toPermissions(roles);
	}
    
    private static Map<String, DataFilter> toRootPermissions(List<Menu> menuList) {
    	Map<String, DataFilter> permissions = new HashMap<>(menuList.size());
    	
    	DataFilter filter = FilterFactory.getRootFilter();
		for (Menu menu : menuList) {
    		if (Objects.isEmpty(menu.getPermission())) {
    			continue;
    		}
    		
    		for (String permission : menu.getPermission().split(",")) {
    			if (Objects.nonEmpty(permission)) {
    				permissions.put(permission, filter);
    			}
    		}
    	}
		
		return permissions;
    }
    
    public User getUser(String id) {




    	return jdbcSession.findById(User.class, id);
    }
    
    public List<Menu> getMenuList() {
    	/*begin 没有缓存直接返回*/
    	EntityCache<Menu> entityCache = jdbcSession.getTemplate(Menu.class);
    	if (entityCache == null) {
    		throw new ORMException("no entity cache found for class:" + Menu.class);
    	}/*end*/
    	
		return jdbcSession.findArray(Menu.class)
					.eq("deleted", false)
					.exe();
    }
    
    private boolean isAdminRole(List<Role> roles) {
    	for (int i = 0; i < roles.size(); i ++) {
    		if (roles.get(i).isAdmin()) {
    			return true;
    		}
    	}
    	
    	return false;
    }
    
    private boolean hasRoot(List<Role> roles) {
    	for (int i = 0; i < roles.size(); i ++) {
    		if (Root.isRoot(roles.get(i).getId())) {
    			return true;
    		}
    	}
    	
    	return false;
    }
    
    private Map<String, DataFilter> toPermissions(List<Role> roles) {
    	Map<String, DataFilter> permissions = new HashMap<>(255);
		for (Role role : roles) {
			boolean change = false;
			
			List<String> menuList = role.getMenus();
			for (int i = 0;  i < menuList.size(); i ++) {
				Menu menu = Pool.get(Menu.class, menuList.get(i));
				if (Objects.isNull(menu) || menu.isDeleted()) {
					menuList.remove(i);
					change = true;
					continue;
				}
				
				for (String permission : menu.getPermission().split(",")) {
	    			if (Objects.isEmpty(permission)) {
	    				continue;
	    			}
	    			
	    			DataFilter filter = permissions.get(permission);
	    			DataFilter nfilter = FilterFactory.getFilter(role.getData_scope());
	    			if (Objects.isNull(filter) ) {
	    				permissions.put(permission, nfilter);
	    				continue;
	    			}
	    			
	    			if (filter.level() > nfilter.level()) {
	    				permissions.put(permission, nfilter);
	    			}
	    		}
			}
			if (change) {
				jdbcSession.updateById(role);
			}
		}
		
		return permissions;
    }
    
    private List<Role> getRoleList() {
    	/*begin 没有缓存直接返回*/
    	EntityCache<Role> entityCache = jdbcSession.getTemplate(Role.class);
    	if (entityCache == null) {
    		throw new ORMException("no entity cache found for class:" + Role.class);
    	}/*end*/
    	
		return jdbcSession.findArray(Role.class)
					.eq("deleted", false)
					.exe();
    }
}
